/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.uoc.tdp.pac3.persistence.dao.impl;

/**
 * Aquesta és la classe que interacciona amb la BD.
 *  @Author TDP
 */


import edu.uoc.tdp.pac3.persistence.exceptions.ErrorConnexioBD;
import edu.uoc.tdp.pac3.persistence.exceptions.ErrorSQL;
import edu.uoc.tdp.pac3.persistence.exceptions.ItemNoTrobat;
import java.sql.*;

public abstract class GestorDisc
{
  /**atribut que indica si s'ha establert o no la connexió amb la BD*/
  boolean connectat;
  /**atribut que indica si hi ha una transacció en execusió*/
  boolean enTransaccio;
  /**atribut que conté l'objecte connexió amb la BD*/
  Connection connexio;
  /**atribut que permet executar sentències SQL contra la BD*/
  Statement sentencia;

  /**
   * Constructor de la classe gestorVotacio. Inicialitza els atributs.
   */
  public GestorDisc() 
  {
    connectat = false;
    enTransaccio = false;
  }

  /**
   * Funció que executa una sentència SELECT a la BD.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha executat el SELECT}.
   * @param nomTaula String que conté el nom de la taula.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws itemNoTrobat si la sentència SELECT no retorna cap fila.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el cursor amb les files trobades.
   */
  public ResultSet buscar(String nomTaula) 
	throws ErrorConnexioBD, ItemNoTrobat, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    ResultSet rs;

    try {
      Statement st = connexio.createStatement();
      rs = st.executeQuery("SELECT * FROM " + nomTaula);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }

    return rs;
  }

  /**
   * Funció que executa una sentència SELECT a la BD especificant una 
   * condicio de cerca.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha executat el SELECT}.
   * @param nomTaula String que conté el nom de la taula.
   * @param condicio String que conté la condició de cerca.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws itemNoTrobat si la sentència SELECT no retorna cap fila.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el cursor amb les files trobades.
   */
  public ResultSet buscar(String nomTaula, String condicio) 
	throws ErrorConnexioBD, ItemNoTrobat, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    ResultSet rs;

    try {
      Statement st = connexio.createStatement();
      String ss = "SELECT * FROM " + nomTaula;
	  if ( condicio != "" ) ss = ss + " WHERE " + condicio;
      rs = st.executeQuery(ss);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }

    try {
      if ( ! rs.next() )
        throw new ItemNoTrobat();
    } catch (SQLException e) {
      throw new ErrorSQL (e.toString());
    }

    return (rs);
  }

  /**
   * Funció que executa una sentència SELECT a la BD especificant els 
   * camps a retornar i una condicio de cerca.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha executat el SELECT}.
   * @param nomTaula String que conté el nom de la taula.
   * @param camps String que conté la llista de camps a retornar.
   * @param condicio String que conté la condició de cerca.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws itemNoTrobat si la sentència SELECT no retorna cap fila.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el cursor amb les files trobades.
   */
  public ResultSet buscar(String nomTaula, String camps, String condicio) 
	throws ErrorConnexioBD, ItemNoTrobat, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    ResultSet rs;

    try {
      Statement st = connexio.createStatement();
      String ss = "SELECT " + camps + " FROM " + nomTaula;
	  if ( condicio != "" ) ss = ss + " WHERE " + condicio;
      rs = st.executeQuery(ss);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }

    try {
      if ( ! rs.next() )
        throw new ItemNoTrobat();
    } catch (SQLException e) {
      throw new ErrorSQL (e.toString());
    }

    return (rs);
  }

  /**
   * Funció que executa una sentència DELETE a la BD.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha executat el DELETE}.
   * @param nomTaula String que conté el nom de la taula.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el nombre de files esborrades.
   */
  public int esborrar(String nomTaula) 
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    int rc;

    try {
      rc = sentencia.executeUpdate("DELETE FROM " + nomTaula);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }

    return rc;
  }

  /**
   * Funció que executa una sentència DELETE a la BD especificant una condicio.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha executat el DELETE}.
   * @param nomTaula String que conté el nom de la taula.
   * @param condicio String que conté la condició per esborrar.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el nombre de files esborrades.
   */
  public int esborrar(String nomTaula, String condicio) 
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    int rc;

    try {
      String ss = "DELETE FROM "  + nomTaula;
	if ( condicio != "" ) ss = ss + " WHERE " + condicio;
      rc = sentencia.executeUpdate(ss);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }

    return rc;
  }

  /**
   * Funció que executa una sentència INSERT a la BD.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha executat l'INSERT}.
   * @param nomTaula String que conté el nom de la taula.
   * @param valors String que conté la llista entre parèntesi dels valors dels camps.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return 1 si acaba correctament o 0 altrament.
   */
  public int insertar(String nomTaula, String valors) 
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    int rc;

    try {
      rc = sentencia.executeUpdate("INSERT INTO " + nomTaula  + " VALUES " + valors);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }

    return rc;
  }

  /**
   * Funció que executa una sentència INSERT a la BD restringint el nombre de
   * camps dels quals s'informa valor.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha executat l'INSERT}.
   * @param nomTaula String que conté el nom de la taula.
   * @param camps String que conté la llista entre parèntesi dels noms dels camps.
   * @param valors String que conté la llista entre parèntesi dels valors dels camps.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return 1 si acaba correctament o 0 altrament.
   */
  public int insertar(String nomTaula, String camps, String valors) 
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    int rc;

    try {
      rc = sentencia.executeUpdate("INSERT INTO " + nomTaula  + 	" " + camps + " VALUES " + valors);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }

    return rc;
  }

  /**
   * Funció que executa una sentència UPDATE a la BD.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha executat l'UPDATE}.
   * @param nomTaula String que conté el nom de la taula.
   * @param camps String que conté la llista de camps i valors separats per comes.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el nombre de files modificades.
   */
  public int modificar(String nomTaula, String camps) 
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    int rc;

    try {
      rc = sentencia.executeUpdate("UPDATE " + nomTaula  + " SET " + camps);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }

    return rc;
  }

  /**
   * Funció que executa una sentència UPDATE a la BD especificant una condicio.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha executat l'UPDATE}.
   * @param nomTaula String que conté el nom de la taula.
   * @param camps String que conté la llista de camps i valors separats per comes.
   * @param condicio String que conté la condició per modificar.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el nombre de files modificades.
   */
  public int modificar(String nomTaula, String camps, String condicio) 
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    int rc;

    try {
      String ss = "UPDATE " + nomTaula  + " SET " + camps;
	if ( condicio != "" ) ss = ss + " WHERE " + condicio;

      rc = sentencia.executeUpdate(ss);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }
 
    return rc;
  }

  /**
   * Funció que compta el nombre de files d'una taula de la BD.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha comptat el nombre de files}.
   * @param nomTaula String que conté el nom de la taula.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el nombre de files de la taula.
   */
  public int count(String nomTaula)
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    ResultSet rs;
    int rc;

    try {
      rs = sentencia.executeQuery("SELECT count(*) FROM " + nomTaula);
      rs.next();
      rc = rs.getInt(1);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }
    return (rc);
  }

  /**
   * Funció que compta el nombre de files d'una taula de la BD segons una condició.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha comptat el nombre de files}.
   * @param nomTaula String que conté el nom de la taula.
   * @param condició String que conté la condició.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el nombre de files de la taula que compleix la condició.
   */
  public int count(String nomTaula, String condicio)
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    ResultSet rs;
    int rc;

    try {
      String ss = "SELECT count(*) FROM " + nomTaula;
	if ( condicio != "" ) ss = ss + " WHERE " + condicio;
      rs = sentencia.executeQuery(ss);
      rs.next();
      rc = rs.getInt(1);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }
    return (rc);
  }

  /**
   * Funció que obté el valor màxim d'un camp d'una taula de la BD.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha obtingut el màxim}.
   * @param nomTaula String que conté el nom de la taula.
   * @param camp String que conté el nom del camp.
   * @throws errorConnexioBD si la connexió amb la BD no està oberta.
   * @throws errorSQL si hi ha algun error en la sentència SQL.
   * @return el màxim.
   */
  public int maxInt(String nomTaula, String camp)
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    ResultSet rs;
    int rc;

    try {
      rs = sentencia.executeQuery("SELECT max(" + camp + ") FROM " + nomTaula);
      rs.next();
      rc = rs.getInt(1);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }
    return (rc);
  }

  /**
   * Procediment que tanca la connexió amb la BD.
   * {Pre: Existeix una connexió amb la BD}.
   * {Post: S'ha tancat la connexió}.
   */
  public void tancarConnexio()
  {
    if ( ! connectat )
      return;

    if ( enTransaccio )
    {
	  enTransaccio = false;	  	
	  try {
        connexio.rollback();
	  } catch (Exception e) {}
    }

    try {
      sentencia.close();
      connexio.close();
    } catch (Exception e) {}

    connectat = false;
  }

  /**
   * Procediment que estableix una connexió amb la BD.
   * {Pre: existeix el nom ODBC per a la connexió a la BD}.
   * {Post: S'ha establert la connexió amb la BD}.
   * @param nomBD String que conté el nom ODBC
   * @throws errorConnexioBD si no s'ha pogut establir la connexió amb la BD 
   */
  public void obrirConnexio(String url, String user, String password) throws ErrorConnexioBD
  {
   // try {
        
     // Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
   // } catch (Exception e) {
   //   throw new ErrorConnexioBD("No s'ha pogut carregar el pont Jdbc-Odbc: " + e.toString());
   // };

    try {
      connexio = DriverManager.getConnection(url, user, password);
      sentencia = connexio.createStatement();
    } catch (Exception e) {
      throw new ErrorConnexioBD("Error en getConnection: " + e.toString());
    };

    connectat = true;
  }

  /**
   * Procediment que inicia una transacció.
   * {Pre: hi ha una connexió a la BD oberta}.
   * {Post: S'ha iniciat una transacció}.
   * @throws errorConnexioBD si no hi ha una connexió amb la BD.
   * @throws errorSQL si hi ha un error en iniciar la transacció.
   */
  public void begin()
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    try {
      connexio.setAutoCommit(false);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }

    enTransaccio = true;
    return;
  }

  /**
   * Procediment que finalitza una transacció acceptant les actualitzacions.
   * {Pre: hi ha una connexió a la BD oberta}.
   * {Post: S'ha tancat la transacció}.
   * @throws errorConnexioBD si no hi ha una connexió amb la BD.
   * @throws errorSQL si hi ha un error en tancar la transacció.
   */
  public void commit()
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    if ( ! enTransaccio )
      return;

    enTransaccio = false;
    try {
      connexio.commit();
      connexio.setAutoCommit(true);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }
    return;
  }

  /**
   * Procediment que finalitza una transacció desfent les actualitzacions.
   * {Pre: hi ha una connexió a la BD oberta}.
   * {Post: S'ha tancat la transacció}.
   * @throws errorConnexioBD si no hi ha una connexió amb la BD.
   * @throws errorSQL si hi ha un error en tancar la transacció.
   */
  public void rollback()
	throws ErrorConnexioBD, ErrorSQL
  {
    if ( ! connectat )
      throw new ErrorConnexioBD("Connexio no establerta.");

    if ( ! enTransaccio )
      return;

    enTransaccio = false;
    try {
      connexio.rollback();
      connexio.setAutoCommit(true);
    } catch (Exception e) {
      throw new ErrorSQL (e.toString());
    }
    return;
  }

  /**
   * Funció que tanca la connexió amb la BD. La crida el garbage collector quan es 
   * detruiex l'objecte.
   * {Pre: No hi ha més referències a l'objecte en memòria}.
   * {Post: S'ha tancat la connexió amb la BD}. 
   */
  protected void finalize()
  {
    tancarConnexio();
    return;
  }

 /* Procediment per fer les proves unitàries de la classe
  public static void main (String[] args) {
  gestorDisc gd = new gestorDisc();
  try {
    gd.obrirConnexio("centralVotSegur");
	System.out.println(gd.insertar("votacioCircums", "(3, 3)"));
	gd.begin();
	System.out.println(gd.insertar("votacioCircums", "(1, 1)"));
	System.out.println(gd.insertar("votacioCircums", "(2, 2)"));
	gd.commit();
    gd.tancarConnexio();
	//System.out.println(gd.insertar("votacioCircums", "(4, 4)"));
  } catch (Exception e) {
    System.out.println (e.toString());
  }
  }*/
}